home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / text1 / texix / texix.web < prev    next >
Encoding:
Text File  |  1991-10-23  |  49.7 KB  |  1,578 lines

  1. % Version 1.3 makes it easier to create other changes files (6/89)
  2. \font\twelvept=cmbx12
  3. \font\tentex=cmr10
  4. \def\topofcontents{\null\vfill\eject
  5.     \def\titlepage{T}
  6.     \centerline{{\twelvept The \TeX IX Index Program}}
  7.     \centerline{{\twelvept For IBM VM/CMS Pascal/VS}}
  8.     \vskip15pt
  9.     \centerline{Version 1.3, June 1989}
  10.     \hbox{\vbox{\hsize\the\hsize This work is
  11.    protected as an unpublished work under U.S. copyright laws.
  12.    Copyright $\copyright$ 1988 by WSUCSC.  All rights Reserved.}}
  13.    \vskip18pt
  14.     \hbox{\vbox{\hsize\the\hsize This software is furnished under a
  15.     license for
  16.     use only on a single computer system and may be copied only
  17.   with the inclusion of the above copyright notice.
  18.  This software, or any other copies
  19.  thereof, may not be provided or otherwise made available to any
  20.  other person except for use on such system and to one who agrees to
  21.  these license terms.  Title to and ownership of the software shall
  22.  at all times remain in WSUCSC.}}
  23.     \vfill}
  24. \let\tentex=\tt
  25. \def\_{\leavevmode \kern.06em \vbox{\hrule width.3em}}%
  26. %
  27. \def\}{\ifmmode \rbrace
  28.           \else $\rbrace$\fi}%
  29. %
  30. \def\{{\ifmmode \lbrace
  31.           \else $\lbrace$\fi}%
  32. %
  33. \def\us#1{$\underline{\smash{\hbox{#1}}}$}%
  34. \def\lin#1{\par
  35.                   \leftskip 0pt
  36.                   \advance \leftskip by #1
  37.                   }%
  38. \def\vs#1{\vskip #1\relax}
  39. \catcode`*=11
  40. % labeled definition macro
  41. %
  42. \newdimen\la*w   \la*w=1in         % Label width
  43. \newtoks\la*g    \la*g={1em}       % Label gutter
  44. \newtoks\la*s    \la*s={12pt}      % Skip before label
  45. \newtoks\la*f    \la*f={\rm}       % Label font
  46. \newbox\tempbox
  47. %
  48. \long\def\label#1{\par\vskip\the\la*s
  49.              \setbox\tempbox=\vtop{\hsize=\la*w
  50.                                   \leftskip=0pt
  51.                                   \rightskip=0pt plus2em
  52.                                   \tolerance=1600
  53.                                   \noindent
  54.                                   \the\la*f
  55.                                   #1}%
  56.              \hangindent=\la*w
  57.              \advance \hangindent by \the\la*g
  58.              \hangafter=1
  59.              \noindent
  60.              \setbox0=\hbox{\rlap{\box\tempbox}}\ht0=0pt\dp0=0pt\box0%
  61.              \hskip\la*w
  62.              \hskip\the\la*g
  63.              \ignorespaces}
  64. \let\la=\label
  65. \catcode`*=12
  66. \let\nin=\noindent
  67. \def\lbr{\null\hfil\break}
  68. \def\PASCAL{{\sc PASCAL/VS}}
  69. \def\hs#1{\hskip#1}
  70. \def\text#1{{\it \TeX T#1}}
  71. \def\9{\it}
  72. \def\bs{$\backslash$}
  73. % Version 1.0; Dean Guenther; 4/1/86
  74. %
  75. @* Introduction.
  76. This procedure was created to give \text1\  the ability to produce a sorted
  77. index in the same one pass, instead of creating the index, sorting it, and
  78. printing the index out in three separate steps.
  79.  
  80. To accomplish this feat, a new control sequence, \bs{}sortindex, was added to
  81. the basic \TeX\ program which gives the \text1\  user up to three sorted
  82. index files per run. The format of this new control sequence is
  83. \par{\tt\indent\bs{}sortindex\it n}
  84. \par
  85. \nin where {\it n} is the index file number: 1, 2, or 3.
  86.  
  87. @ The @^TEXIX@>
  88. program is written entirely in WEB, except for an external
  89. procedure, {\it plsort}, which is written in @^PL/1@> PL/1.
  90. This was necessary since
  91. \PASCAL\  cannot call
  92. @:plsort@>
  93. Syncsort @^SYNCSORT@>
  94. to do an internal sort, and PL/1 can.
  95.  
  96. @
  97. Also, it should be pointed out early that I made use of \PASCAL'
  98. nifty string handling capability, in particular, the |string|,
  99. |index| and |substr| functions. If needed, these should not be too
  100. difficult to translate into another \PASCAL\
  101. compiler (famous last words).
  102.  
  103. @p
  104. segment texix;
  105. procedure texix(ix:char); external;
  106. procedure texix;
  107. const @<Global Constants@>
  108. type @<Global Types@>
  109. var @! m,k,i,j :integer;@/
  110. @<Global Variables@>
  111.  
  112. @ The file {\it ix\_file}
  113. is the file opened for output in the \text1\  session
  114. itself. It will be closed in TEXIX, opened for input, closed again, and
  115. finally opened for output where the final sorted index file will be
  116. written to, complete with all \text1\  markup necessary for printing
  117. the index.
  118.  
  119. @<Global Var...@>=
  120. sysprint:text;
  121. @!ix_file :text;
  122.  
  123. @ The {\it sort\_file} is used for the internal sort only. The \text1\  user
  124. never has access to it.
  125.  
  126. @<Global Var...@>=
  127. @!sort_file :file of sort_type;
  128.  
  129. @* Macros and definitions.
  130. Here are some macros and definitions used throughout.
  131.  
  132. @d incr(#) == # := # + 1
  133. @d decr(#) == # := # - 1
  134.  
  135. @d othercases == otherwise
  136. @d endcases == end;
  137.  
  138. @d dosubstr == substr
  139. @d doindex == index
  140.  
  141. @f othercases == else
  142. @f endcases == end
  143.  
  144. @d getout == return
  145. @d messages == sysprint
  146.  
  147. @d max_field = 300
  148. @d max_levels = 3
  149. @d max_lrecl = 2048
  150.  
  151. @d remove_characters(#) == if length(in_record) > # then
  152. in_record := dosubstr(in_record,1 + #)
  153. else in_record := ''
  154.  
  155. @d do_nothing == begin end
  156.  
  157. @d do_sort_as == begin remove_characters(6);
  158. i := doindex(in_record,'{$}');
  159. write_sort_chars(i-1);
  160. remove_characters(3);
  161. end
  162.  
  163. @d check_case == begin
  164. if not respect_case then for j:= 1 to
  165. sort_part[i].field_lngth do sort_part[i].field_level[j] :=
  166. upper_case(sort_part[i].field_level[j]); end
  167.  
  168. @d string_type(#) == string(#)
  169.  
  170. @d ccat_temp ==  @t\hs{2em}@>@=||@>@t\hs{2em}@>
  171.  
  172.  
  173. @d add_comma(#) == # := ccat(#,',')
  174. @d add_cmma_blnk(#) == # := ccat(#,', ')
  175.  
  176. @d do_when_bold == begin
  177. if length(bold_string) > 0 then add_comma(bold_string);
  178. bold_string:=
  179. ccat(ccat(ccat(bold_string,'{\bd '),curr_str_page),'}');
  180. @.\bs{}bd@>
  181. end
  182.  
  183. @d do_when_underscore == begin
  184. if length(us_string) > 0 then add_comma(us_string);
  185. us_string:=
  186. ccat(ccat(ccat(us_string,'\us{'),curr_str_page),'}');
  187. @.\bs{}us@>
  188. end
  189.  
  190. @d do_when_italic == begin
  191. if length(rm_string) > 0 then add_comma(rm_string);
  192. rm_string:=
  193. ccat(ccat(ccat(rm_string,'{\it '),curr_str_page),'}');
  194. @.\bs{}it@>
  195. end
  196.  
  197. @d do_when_roman == begin
  198. if length(rm_string) > 0 then add_comma(rm_string);
  199. rm_string:=ccat(rm_string,curr_str_page);
  200. end
  201.  
  202. @d do_write(#)==write(#)
  203.  
  204. @d do_lnwrite(#)==writeln(#)
  205.  
  206. @ This is arbitrary, but
  207. there should never be more than 8 indicies.
  208.  
  209. @d s_file_number == '9'
  210.  
  211. @ This is used to print the subentry1 header if there are no page
  212. numbers under its subentry and there are under subentry2.
  213.  
  214. @d id2_missing_check==
  215. curr_level:=2;
  216. if sort_record.sort_part[3].field_lngth=0
  217. then do_nothing else begin
  218. id2_is_missing:=true;
  219. write_header(sort_record.print[2].field_lngth,
  220. sort_record.print[2].field_level);
  221. id2_is_missing:=false;
  222. curr_level:=3;end
  223.  
  224. @ This is used to print the primary header if there are no page
  225. numbers under the primary header, and there are page numbers under
  226. either subentry1 or subentry2.
  227.  
  228. @d id1_missing_check==if sort_record.sort_part[2].field_lngth = 0
  229. then begin curr_level:=1;
  230. if sort_record.sort_part[3].field_lngth = 0
  231. then do_nothing else begin
  232. id1_is_missing:=true;
  233. write_header(sort_record.print[1].field_lngth,
  234. sort_record.print[1].field_level);
  235. id1_is_missing:=false;
  236. curr_level:=3;end;end
  237. else begin curr_level:=1;
  238. id1_is_missing:=true;
  239. write_header(sort_record.print[1].field_lngth,
  240. sort_record.print[1].field_level);
  241. id1_is_missing:=false;
  242. id2_missing_check;
  243. end
  244.  
  245. @ This is a little macro used to access {\it write\_header}.
  246.  
  247. @d write_prev_header==
  248. begin case curr_level of
  249. 1: write_header(prev_ln1,prev_pn1);
  250. 2: write_header(prev_ln2,prev_pn2);
  251. 3: write_header(prev_ln3,prev_pn3);
  252. othercases do_nothing
  253. endcases
  254. end;
  255.  
  256. @ The function ccat will concatenate two strings together.
  257.  
  258. @^System dependent code@>
  259. @p function ccat(x,y:string_pass):string_pass;
  260. begin
  261.   ccat := x ccat_temp y;
  262. end;
  263.  
  264. @* Opening and Closing the files.
  265. This is all very dependent on \PASCAL.
  266.  
  267. @ The {\it reset\_file} procedure is used to open all files.
  268. One time this is done when first entering TEXIX.
  269. Note that the file name is ``{\tt IX\it n}'' where ``{\it n}'' is
  270. 1--3. This can easily be extended to 4--8 by chaning the \bs{}index
  271. markup in TEXT1@@.
  272. The other time the open is done is when opening the sort file, after it
  273. has already been sorted.
  274. This is system dependent. The following will work for \PASCAL.
  275.  
  276. @^System dependent code@>
  277. @p procedure reset_file(file_number:char);
  278. begin
  279. if file_number = '9' then reset(sort_file,
  280. 'NAME=TEXT1$$.OUTSORT.A,LRECL=1857,RECFM=V')
  281. else reset(ix_file,
  282. ccat(ccat('NAME=TEXT1$$.IX',str(file_number)),'.A'));
  283. end;
  284.  
  285. @
  286. The {\it sort\_file} is opened for output to write the sort records to.
  287. The {\it ix\_file} is opened for output after the sort records have
  288. been sorted and processed again.
  289. This is system dependent. The following will work for \PASCAL.
  290.  
  291. @^System dependent code@>
  292. @p procedure file_rewrite(file_number:char);
  293. begin
  294. if file_number = '9'
  295. then
  296. rewrite(sort_file,'NAME=TEXT1$$.INSORT.A,LRECL=1857,RECFM=V')
  297. else
  298. rewrite(ix_file,
  299. ccat(ccat('NAME=TEXT1$$.IX',str(file_number)),
  300. '.A,LRECL=2048,RECFM=V'));
  301. end;
  302.  
  303. @* The Sort Record Description. The record type called
  304. {\it sort\_type} is the record written to the {\it sort\_file}.
  305. If consists of the following:
  306. \la{\bf Bytes}
  307. \la{1}
  308. A one byte {\it record\_type} (0={\bf bold page number};
  309. 2= roman page number (the default); 4=\us{underscored} \us{page}
  310. \us{number};
  311. 6={\it italic page number}; 9=blind entry).
  312. \la{2--5}
  313. The integer page number. If the page
  314. number is in the preface part of the document, then the page number will
  315. be negative.
  316. \la{6--9}
  317. Used in sorting decending. That means that -1 will be at the top.
  318. If the page number is plus to begin with, then this is set to zero
  319. for sorting, so it will be at the bottom of the list.
  320. \la{10-29} This is the same as page number, unless the page number is
  321. negative, in which case this field is a roman numeral.
  322. \la{30--33\lbr34--333\lbr334--337\lbr338--637\lbr638--641\lbr642--941}
  323. The sort field. There are three arrays in this field. Each array
  324. consists of a 4 byte length, followed by the {\it field\_array} which is
  325. the length of the sort entry or subentry.
  326. It is these fields which will get sorted by @^Syncsort@> Syncsort.
  327. If there was a {\it sort\_as}
  328. used for a sort entry or subentry, that value passed in the {\it sort\_as}
  329. is placed here.
  330. \vs{24pt}
  331. \la{942--945\lbr946--1245\lbr1246--1249\lbr
  332. 1250--1549\lbr1550--1553\lbr1554--1853}
  333. The print field.
  334. The description is the same as the sort field.
  335. This is the way the index entry will print, but not necessarily how
  336. it will sort.
  337. \vs{96pt}
  338.  
  339. @ @<Global Constants@>=
  340. @!max_pn_alpha=20;
  341. @!max_pnum=9;
  342.  
  343. @ @<Global Types@>=
  344. @!string_pass=string_type(max_lrecl);
  345. @!pass_pn_alpha=string_type(max_pn_alpha);
  346. @!pn_type=packed array[1..max_pnum] of char;
  347. @!pn_alpha_type=packed array[1..max_pn_alpha] of char;
  348. @!field_array  = packed array[1..max_field] of char;
  349. @!field_type = packed record@|
  350. @!field_lngth :integer;
  351. @!field_level  :field_array;
  352. end;
  353. @!sort_type = packed record@/
  354. @!record_type :char;
  355. @!page_number :integer;
  356. @!abs_page_number :integer;
  357. @!page_string :pn_alpha_type;
  358. @!sort_part :packed array[1..3] of field_type;
  359. @!print :packed array[1..3] of field_type;
  360. end;
  361.  
  362. @* Subroutines. For many of the following subroutines, the following
  363. global variable, {\it current\_level} is needed to indicate what
  364. index level we are presently processing.
  365. 0 = the primary index; 1 = subentry 1, and
  366. 2 = subentry 2.
  367. {\it on\_a\_roll} is `true' if we have a series of consecutive page
  368. numbers going. `false' otherwise.
  369. In other places, {\it print\_style}=1 for the paragraph style; it is
  370. 2 for the dash style; and it is 3 for the indent style.
  371.  
  372. @<Global Var...@>=
  373. @!curr_level :integer;
  374. @!on_a_roll:boolean;
  375.  
  376. @ The {\it write\_header} procedure will write out the primary and
  377. subentry level titles from {\it sort\_record.print}.
  378.  
  379. @p procedure write_header(print_length:integer;print_field:field_array);
  380. var i:integer;
  381. begin
  382. case curr_level of
  383. 1: begin
  384.   do_write(ix_file,'\goodbreak\hp ');
  385. @.\bs{}leavevmode@>
  386. @.\bs{}goodbreak@>
  387. @.\bs{}hp@>
  388.   for i:=1 to print_length do do_write(ix_file,print_field[i]);
  389.   case print_style of
  390.    '1':begin
  391.        do_write(ix_file,', ');
  392.        end;
  393.    '2','3':begin
  394.        if id1_is_missing or dot_leadering
  395.           then do_lnwrite(ix_file,' ')
  396.           else do_lnwrite(ix_file,', ');
  397.        end;
  398.    othercases do_nothing
  399.    endcases; end;
  400. 2: begin case print_style of
  401.    '1':do_nothing;
  402.    '2':begin
  403.          do_write(ix_file,'\indentsubentry',ix,'1---');
  404. @.\bs{}indentsubentry@>
  405.          end;
  406.    '3':begin
  407.          do_write(ix_file,'\indentsubentry',ix,'1');
  408.        end;
  409.    othercases do_nothing
  410.    endcases;
  411.   for i:=1 to print_length do do_write(ix_file,print_field[i]);
  412.   if (print_style <> '1') or id2_is_missing or dot_leadering
  413.      then do_lnwrite(ix_file,' ')
  414.      else do_lnwrite(ix_file,', ');
  415.   end;
  416. 3: begin case print_style of
  417.    '1':do_nothing;
  418.    '2':begin
  419.          do_write(ix_file,'\indentsubentry',ix,'2---');
  420.          end;
  421.    '3':begin
  422.          do_write(ix_file,'\indentsubentry',ix,'2');
  423.        end;
  424.    othercases do_nothing
  425.    endcases;
  426.   for i:=1 to print_length do do_write(ix_file,print_field[i]);
  427.   if dot_leadering
  428.   then do_lnwrite(ix_file,' ')
  429.   else do_lnwrite(ix_file,', ');
  430.   end;
  431. othercases do_nothing
  432. endcases
  433. end;
  434.  
  435. @ The {\it numeric} function will take a packed array of length
  436. max\_pnum and convert
  437. that array (which is really the page number) into an integer.
  438.  
  439. @p function numeric(simple_array:pn_type):integer;
  440. var @!i,j_mult,pn:integer;
  441. begin pn := 0;
  442. i := max_pnum;j_mult:=1;
  443. repeat
  444. if i = 1 then if simple_array[1] = '-'
  445. then pn := -1 * pn
  446. else pn := pn + ((ord(simple_array[1])-ord('0')) * j_mult)
  447. else pn := pn + ((ord(simple_array[i])-ord('0')) * j_mult);
  448. j_mult := j_mult * 10;
  449. decr(i);
  450. until i < 1;
  451. numeric:=pn;
  452. end;
  453.  
  454. @ The {\it get\_numeric}
  455. function will take a packed array of length max\_pn\_alpha and convert
  456. that array (which is really the page number) into an integer.
  457.  
  458. @p function get_numeric(x_string:string_type(max_pn_alpha)):integer;
  459. var @!i:integer;
  460. begin
  461. readstr(x_string,i);
  462. get_numeric:=i;
  463. end;
  464.  
  465. @ The {\it strvalue} function takes an integer and converts it into
  466. a string.
  467.  
  468. @^System dependent code@>
  469. @p procedure strvalue(x:integer; var results:pass_pn_alpha);
  470. var
  471. temp:string_type(max_pn_alpha);
  472. begin
  473. writestr(temp,x);
  474. results:=ltrim(temp);
  475. end;
  476.  
  477. @ The {\it strconv} function takes an array and converts it into
  478. a string.
  479.  
  480. @p function strconv(x:pn_alpha_type):string_type(max_pn_alpha);
  481. var i:integer;
  482. temp:string_type(max_pn_alpha);
  483. begin
  484. temp:='';i := 1;
  485. with sort_record do begin
  486. repeat
  487. if x[i] <> ' ' then
  488. temp := ccat(temp,str(x[i]));
  489. incr(i);
  490. until (i > max_pn_alpha) or (x[i] = ' ');
  491. end;
  492. strconv:=temp;
  493. end;
  494.  
  495. @ The procedure {\it write\_print\_chars} will write the number of
  496. characters indicated
  497. in the parm field to the appropriate print field. There are three levels of
  498. print fields. Level 0 is the primary index, level 1 is the subentry 1 index,
  499. and level 2 is the subentry 2 index. After writing the number of characters
  500. to the print field, that number of characters is removed from the input record.
  501. You might notice that this procedure is very similar to the
  502. write\_sort\_chars
  503. procedure. The only difference in the two is that this procedure deals
  504. with how the index is
  505. to be printed after sorting. The former deals with how the
  506. index is to be sorted.
  507. Also, this procedure is called by {\it write\_sort\_chars}.
  508.  
  509. @^System dependent code@>
  510. @p procedure write_print_chars(number_of_characters:integer);
  511. var m:integer;
  512. begin with sort_record.print[curr_level+1] do
  513. for m := 1 to number_of_characters do begin
  514. incr(field_lngth);
  515. field_level[field_lngth] := in_record[m];
  516. end;
  517. remove_characters(number_of_characters);
  518. end;
  519.  
  520. @ The procedure {\it write\_sort\_chars}
  521. will write the number of characters indicated
  522. in the parm field to the appropriate sort field. There are three levels of
  523. sort fields. Level 0 is the primary index, level 1 is the subentry 1 index,
  524. and level 2 is the subentry 2 index. After writing the characters to
  525. the sort fields, they will be removed from the input string.
  526.  
  527. @^System dependent code@>
  528. @p procedure write_sort_chars(number_of_characters:integer);
  529. var m:integer;
  530. begin with sort_record.sort_part[curr_level+1] do
  531. for m := 1 to number_of_characters do begin
  532. incr(field_lngth);
  533. field_level[field_lngth] := in_record[m];
  534. end;
  535. remove_characters(number_of_characters);
  536. end;
  537.  
  538. @
  539. This procedure processes subentries within the \bs{}index command. For example,
  540. the phrase ``Mt. St. Helens'' would be processed here for the entry created
  541. by the markup:
  542. \vs{12pt}
  543. {\lin{.5in}
  544. {\tt\bs{}index\{volcanos\bs{}subentry1\{Mt. St. Helens\}\}}\par}
  545. \vs{12pt}
  546. \nin Note
  547. that there are two levels of subentries, \bs{}subentry1 and \bs{}subentry2.
  548. You can
  549. use \bs{}sortas within a \bs{}subentry. But you cannot use \bs{}subentry2 within
  550. \bs{}subentry1 and vise versa. If you think it should be permitted, don't.
  551. Also, \bs{}blindentry is not permitted within \bs{}subentry.
  552. (I can't be flexible {\bf everywhere}.)
  553.  
  554. @ First save the current level, then set the current level to the
  555. subentry level. Process until the subentry is completely digested.
  556.  
  557. @^System dependent code@>
  558. @p procedure process_subentry(@!entry_level:integer);
  559. var temp_level :integer;
  560. digest :boolean;
  561. begin remove_characters(1);{Throw away the `1' or `2'}@/
  562. temp_level := curr_level;
  563. curr_level := entry_level;
  564. digest := true;
  565. repeat
  566. @<Digest Subentry@>
  567. until not digest;
  568. curr_level := temp_level;
  569. end;
  570.  
  571. @ If the next character is a dollar sign, then we {\bf might} have a \bs{}sortas
  572. If the next character is a left curly brace, then check to see if we've
  573. reached the end of this subentry. If the character is not `\$' or `\{',
  574. then write the character to the print file.
  575.  
  576. @<Digest Subentry@>=
  577. if in_record[1] = '$'
  578. then @<Check for Sortas@>
  579. else if in_record[1] = '{'
  580. then @<Check for Subentry End@>
  581. else write_print_chars(1);
  582.  
  583. @ If the next two characters are `\$\}' then we are are the end of
  584. the subentry.
  585.  
  586. @<Check for Subentry End@>=
  587. begin
  588. if (in_record[2] = '$') and (in_record[3] = '}')
  589. then begin
  590. digest := false;
  591. remove_characters(3);
  592. end
  593. else write_print_chars(1);
  594. end
  595.  
  596. @ If the next three are `\{\$\}' then we {\it might} have a sortas.
  597. and check the next two
  598. characters to see if they
  599. are `{\tt sa}'. If so, we {\bf do} have a sortas.
  600. Otherwise write those 6 characters out. In any case, if we don't have
  601. a `\{\$\}' to begin with, then write the first character out
  602. (which was a
  603. `\$' if you remember from earlier.)
  604.  
  605. @<Check for Sortas@>=
  606. if (in_record[2] = '{') and
  607. (in_record[3] = '$') and (in_record[4] = '}')
  608. then begin
  609. if (in_record[5] = 's') and (in_record[6] = 'a')
  610. then do_sort_as
  611. else write_print_chars(6);
  612. end
  613. else write_print_chars(1)
  614.  
  615. @
  616. The  {\it equal\_arrays}
  617. function returns a true if the two arrays being passed are
  618. identical. False if not.
  619.  
  620. @p function equal_arrays(fieldy:field_array;fieldz:field_array):boolean;
  621. var i :integer;
  622. still_checking :boolean;
  623. begin i := 1;
  624. still_checking := true;
  625. repeat
  626. if fieldy[i] <> fieldz[i] {if arrays not equal}
  627. then still_checking := false
  628. else incr(i);
  629. until (i>max_field) or not still_checking;
  630. equal_arrays := still_checking;
  631. end;
  632.  
  633. @ The {\it plsort} procedure is written in PL/1 to call the CMS sort program
  634. Syncsort. This was necessary since you cannot call Syncsort from PASCAL/VS.
  635. The fields to be sorted are defined internally in the {\it plsort}
  636. program. This should be the same as the three sort fields in {\it sort\_type}.
  637. @:sort_type@>
  638. @:plsort@> @^PL/1@>
  639. @^Syncsort@>
  640.  
  641. @p procedure plsort(var sort_rc:integer); fortran;
  642.  
  643. @ This function converts to all uppercase.
  644. Notice that this is an EBCIDIC conversion, not an ASCII conversion
  645. to uppercase.
  646.  
  647. @p function upper_case(x:char):char;
  648. var temp:char;
  649. begin
  650.   if (ord(x)>=129)and(ord(x)<=169) then
  651.       temp:=chr(ord(x)+64)
  652.   else temp:=x;
  653. upper_case:=temp;
  654. end;
  655.  
  656.  
  657. @* Read Each Index Entry.
  658. Each line in the {\it ix\_file} is a separate index entry generated with the
  659. \bs{\it index markup}. This markup has the format
  660. \par{\tt\bs{}index\it n\tt\{\it entry\rm/\it markup\tt\}}
  661. \par
  662. \nin where ``{\it entry}''
  663. is the textual material of the primary index. The
  664. ``{\it markup}'' may be one of the following submarkup
  665. which may {\bf only}
  666. appear within the \bs{\it index markup}. Never outside of it.
  667. \item{1.} {\tt\bs{}subentry{\it n\/}}: where {\it n} is ``1'' or ``2'',
  668. denoting the subentry level.
  669. You would use this submarkup in the following fashion:
  670. \par{\lin{+.5in}\tt\bs{}index1\{\rm entry
  671. \bs\rm subentry1\tt\{\it submarkup\tt\}\}\par}
  672. \indent where {\it submarkup} here can only be \bs{\it sortas}.
  673. \item{2.}{\tt\bs{}sortas}: This markup is used to indicate text for either the
  674. primary sort level, or one of the two subentry sort levels that is to be
  675. sorted, but not printed in the resulting index. For example, someone may
  676. want ``10 Downing Street''
  677. to sort as ``ten Downing Street''. This would be accomplished by entering
  678. ``{\tt\bs{}index1\{10 Downing Street\bs{}sortas\{ten Downing Street\}\}}''.
  679. You can also use \bs{\it sortas} within the \bs{\it subentry} submarkup.
  680. \item{3.}
  681. {\tt\bs{}itpn}: This markup indicates this index number
  682. is to be printed in italics in the sorted index.
  683. To use it, enter:
  684. \par{\lin{+.5in}\tt\bs{}index1\{\rm entry \tt
  685. \bs{}itpn\}\par}
  686. \item{4.} {\tt\bs{}bdpn}: This markup indicates this
  687. index number is to be
  688. printed in bold in the sorted index.
  689. To use it, enter:
  690. \par{\lin{+.5in}\tt\bs{}index1\{\rm entry \tt
  691. \bs{}bdpn\}\par}
  692. \item{5.} {\tt\bs{}uspn}: This markup indicates
  693. this index number is to
  694. be underscored in the sorted index.
  695. To use it, enter:
  696. \par{\lin{+.5in}\tt\bs{}index1\{\rm entry \tt
  697. \bs{}uspn\}\par}
  698. \item{6.} {\tt\bs{}blindentry}: This markup indicates this index is to be
  699. a blind entry or ``cross reference''. It may appear inside of a
  700. standard entry or subentry. There is no page number associated with
  701. this submarkup.
  702. \par
  703.  
  704. @ @<Global Var...@>=
  705. @!respect_case :boolean;
  706. @!dot_leadering :boolean;
  707. @!print_style:char;
  708. @!sort_record :sort_type;
  709. @!in_record :string_type(max_lrecl);
  710. @!temp_strvalue:pass_pn_alpha;
  711.  
  712. @^System dependent code@>
  713. @ This procedure will read the {\it ix\_file} in until all index requests
  714. have been read.
  715.  
  716. @p procedure read_all_entries;
  717. var @!i,j,k:integer;
  718. begin
  719. @<Get Default Index Values@>
  720. while not eof(ix_file) do begin
  721. @<Initialize Sort Records@>@/
  722. @<Read Next Record@>@/
  723. @<Move Sort Type to Sort Record@>@/
  724. @<Move Page Number to Sort Record@>@/
  725. @<Digest the Input Record@>@/
  726. @<Fill in Sort Fields@>@/
  727. @<Write Sort Record@>@/
  728. end;end;
  729.  
  730. @ The first index default variable passed is ``case=". A ``yes" value will
  731. respect the case in an index sort field. ``no" (the default) will convert
  732. everything to uppercase before sorting.
  733.  
  734. The second index default ``style=" will be one of ``1", ``2'', or ``3''
  735. depending on whether the style is ``paragraph'', ``dash'', or ``indented''
  736. (which is the default).
  737.  
  738. The third default read in here is the ``leadering=". A ``yes'' here will give
  739. dot leadering. Guess what ``no'' does.
  740. Dot leadering in the paragraph style ({\it print\_style}=1). If that
  741. has been requested, change {\it dot\_leadering} to false.
  742.  
  743. @<Get Default Index Values@>=
  744. readln(ix_file,in_record);
  745. if dosubstr(in_record,doindex(in_record,'=')+1) = 'no'
  746. then respect_case := false
  747. else respect_case := true;
  748. readln(ix_file,in_record);@/
  749. print_style:=in_record[doindex(in_record,'=')+1];@/
  750. readln(ix_file,in_record);@/
  751. if dosubstr(in_record,doindex(in_record,'=')+1) = 'no'
  752. then dot_leadering := false
  753. else dot_leadering := true;@/
  754. if dot_leadering and (print_style = '1')
  755. then begin writeln(messages,
  756. '<TEXT1> Error! Dot leadering not permitted with the paragraph style.');
  757. writeln(messages,'        Dot leadering will be disabled.');
  758. dot_leadering := false;
  759. end;
  760.  
  761. @ The {\it all\_blanks} variable is filled with all blanks. Other arrays
  762. of the same dimension of {\it all\_blanks} can be set to blank themselves
  763. by saying $array\leftarrow all\_blanks$.
  764.  
  765. @<Global Var...@>=
  766. @!all_blanks:field_array;
  767.  
  768. @ @<Initialize Sort Records@>=
  769. with sort_record do
  770. for i := 1 to max_levels do begin
  771. sort_part[i].field_level:= all_blanks;
  772. sort_part[i].field_lngth := 0;
  773. print[i].field_level:= all_blanks;
  774. print[i].field_lngth := 0;
  775. end;
  776. curr_level := 0;
  777.  
  778. @ Read the next index request to be processed.
  779. Get rid of trailing blanks.
  780.  
  781. @<Read Next Record@>=
  782. readln(ix_file,in_record);
  783. in_record:=trim(in_record);
  784.  
  785. @ The {\it sort\_type} is always in column 2. Column 1, 3 and 4 will always
  786. be curly braces. (`\{{\it s}\}\{' where ``{\it s}'' is the sort type.)
  787. After moving the {\it sort\_type} to the sort record, then
  788. delete the {\it sort\_type} and the three curly braces.
  789.  
  790. @<Move Sort Type to Sort Record@>=
  791. sort_record.record_type := in_record[2];
  792. remove_characters(4);
  793.  
  794. @ The end of the page number is the next right curly brace (`\}').
  795. Move all of the digits from the input record to the sort record,
  796. starting with the last digit and going forward. If a minus sign (`-')
  797. is in
  798. the page number (preface material), then put a minus sign in
  799. byte one of the sort record's page number.
  800.  
  801. @<Global Var...@>=
  802. @!in_page_number:pn_type;
  803.  
  804. @ Find and move the page number to the sort record. If it is negative,
  805. then its a roman numeral. In that case, move the page number to the
  806. {\it abs\_page\_number} to be sorted descending, as negative numbers
  807. should be. If its not negative, then move zero to {\it abs\_page\_number}
  808. so it will move to the bottom of that sort field.
  809. After it is moved, delete the appropriate number of characters to
  810. finish digesting the entry.
  811.  
  812. @<Move Page Number to Sort Record@>=
  813. k := doindex(in_record,'}'); {find the end of the page number}
  814. i := k - 1;
  815. for j := 1 to max_pnum do in_page_number[j] := '0';
  816. j := max_pnum;
  817. repeat
  818. if in_record[i] = '-'
  819. then begin
  820. in_page_number[1] := '-';
  821. i := 0;
  822. end
  823. else begin
  824. in_page_number[j] := in_record[i];
  825. decr(i);
  826. decr(j);
  827. end;
  828. until i <= 0;
  829. remove_characters(k+1);@/
  830. sort_record.page_number:=numeric(in_page_number);@/
  831. if sort_record.page_number < 0
  832. then sort_record.abs_page_number:=abs(sort_record.page_number)
  833. else sort_record.abs_page_number:=0;
  834. k := doindex(in_record,'}'); {find the end of the page number string}
  835. for i := 1 to max_pn_alpha do sort_record.page_string[i] := ' ';
  836. for i := 1 to k-1 do
  837. sort_record.page_string[i] := in_record[i];
  838. remove_characters(k);
  839.  
  840. @^System dependent code@>
  841. @ Process the rest of the input record. Each time you see a dollar sign,
  842. there could be a submarkup coming, so examine the next three characters.
  843.  
  844. @<Digest the Input Record@>=
  845. repeat
  846. if (in_record[1] = '$') and (length(in_record) >= 4)
  847. then @<Examine Next 3 Chars@>
  848. else write_print_chars(1);
  849. until length(in_record) < 1;
  850.  
  851. @ Once the index request has been digested, each sort field is checked
  852. to see if it is blank. If so, then {\it sort\_as} was not used, so copy the
  853. {\it print} field to the {\it sort} field.
  854.  
  855. @<Fill in Sort Fields@>=
  856. with sort_record do begin
  857. for i := 1 to max_levels do
  858. if sort_part[i].field_lngth=0
  859. then begin
  860. sort_part[i].field_level := print[i].field_level;
  861. sort_part[i].field_lngth := print[i].field_lngth;
  862. check_case; end
  863. else check_case;
  864. end;
  865.  
  866. @^System dependent code@>
  867. @ Ok, the record has been processed, so write it to the output file.
  868.  
  869. @<Write Sort Record@>=
  870. sort_file@@:=sort_record;
  871. put(sort_file);
  872.  
  873. @ Ok, a dollar sign signals the beginning of some sort of action code if the
  874. next three characters are `\{\$\}'. So lets look at them and see.
  875.  
  876. @<Examine Next 3 Chars@>=
  877. begin if (in_record[2] = '{') and
  878. (in_record[3] = '$') and (in_record[4] = '}')
  879. then @<Examine Next 2 Chars@>
  880. else write_print_chars(1);{Well then, the dollar sign must be part of the index}
  881. end
  882.  
  883. @ If a {\it blindentry} (`{\tt be}') or {\it subentry} (`{\tt se}'),
  884. then perform the appropriate sections.
  885.  
  886. @<Examine Next 2 Chars@>=
  887. begin if (in_record[5] = 'b') and (in_record[6] = 'e')
  888. then @<Do Blind Entry@>
  889. else if (in_record[5] = 's') and (in_record[6] = 'e')
  890. then @<Do Sub Entries@>
  891. else if (in_record[5] = 's') and (in_record[6] = 'a')
  892. then do_sort_as
  893. else write_print_chars(6);{Might as well write all 6 characters examined}
  894. end
  895.  
  896. @ Everything up to the next `\{\$\}' is the blind entry.
  897. The page number for a blind entry will always be 999999999.
  898.  
  899. @<Do Blind Entry@>=
  900. begin remove_characters(6);{Throw away the `\$\{\$\}be'}@/
  901. i := doindex(in_record,'{$}');
  902. curr_level:=2;
  903. write_print_chars(i-1);
  904. curr_level:=0;
  905. remove_characters(3);{Throw away the `\{\$\}'}
  906. sort_record.page_number := 999999999;
  907. end
  908.  
  909. @ Process the subentry, depending on whether or not it is
  910. {\it subentry}1 or {\it subentry}2. If not, ignore the whole thing.
  911.  
  912. @<Do Sub Entries@>=
  913. begin remove_characters(6); {Throw away the `\$\{\$\}se'}
  914. if in_record[1] = '1'
  915. then process_subentry(1)
  916. else if in_record[1] = '2'
  917. then process_subentry(2)
  918. else write_print_chars(1);{Perhaps someone entered \bs{}subentry3??}
  919. end
  920.  
  921. @* Write Formated Index. This section of code takes the sorted index
  922. entries, merges them together, and writes them back out to the index file
  923. to be read in by \bs{}printindex in the user's program. This section consists
  924. of three procedures for accomplishing this feat: {\it add\_page\_number},
  925. {\it digest\_the\_line}, and {\it read\_sorted\_records}.
  926. {\it numeric} is a function to convert the character string page number into
  927. an integer.
  928.  
  929. @^System dependent code@>
  930. @ The procedure {\it add\_page\_number} is used to add the page number of the
  931. current sorted record to the {\it string\_bold} if a
  932. \bs{\it bdpn}, or to
  933. the {\it string\_underscore} if a \bs{\it uspn},
  934. or otherwise to
  935. the {\it str\_build}.
  936.  
  937. @p procedure add_page_number;
  938. var i:integer;
  939.  
  940. @ @<Global Var...@>=
  941. @!prev_page_number:integer;
  942. @!prev_pg_string:string_type(max_pn_alpha);
  943. @!prev_record_type:char;
  944.  
  945. @ First check to see if we have a new page number. If so, then add the page
  946. number to the string. Otherwise, only add the page number if the sort type
  947. is new and is not equal to ``6'' (italic).
  948.  
  949. @p
  950. begin
  951. with sort_record do if prev_page_number = page_number
  952. then if (prev_record_type = record_type) and (record_type <> '9')
  953.  then getout
  954. else if record_type = '6' then getout;
  955. @<Add Page Number to the String@>
  956. end;
  957.  
  958. @ {\it str\_build} is used to accumulate all of the page numbers.
  959. {\it str\_blind\_entry} is used to accumulate the blind entry information
  960. (typically there should be no more than one, but allowance is made for more.)
  961.  
  962. @<Global Var...@>=
  963. @!str_build:string_type(max_lrecl);
  964. @!str_blind_entry:string_type(max_lrecl);
  965. {come on, who's going to have one that long?}
  966.  
  967. @ This module will take the page number of the current sorted index
  968. record and add it to the build string, unless the record type is
  969. 9, which is a blind entry, in which case it is added to the blind entry
  970. string. Note that if the record type is 0 (bold), 4 (underscore) or
  971. 6 (italic), then the page number is prefaced with ``B'', ``U'' or
  972. ``I'' respectively. This identifies that the page number is to be
  973. emphasized when the build string is processed through the
  974. {\it digest\_the\_line} procedure.
  975.  
  976. @<Add Page Number to the String@>=
  977. if sort_record.record_type = '9' then @<Do Sorted Blind Entry@>
  978. else begin @<Do Sorted Non Blind Entry@>
  979. end;
  980. prev_page_number := sort_record.page_number;@/
  981. prev_pg_string := strconv(sort_record.page_string);@/
  982. prev_record_type := sort_record.record_type;
  983.  
  984. @ @<Do Sorted Blind Entry@>=
  985. with sort_record.print[3] do begin
  986. if length(str_blind_entry) > 0 then add_cmma_blnk(str_blind_entry);
  987. for i := 1 to field_lngth do
  988. str_blind_entry := ccat(str_blind_entry,str(field_level[i]));
  989. end
  990.  
  991. @ If the {\it page\_string[1]} is less than zero, its alphabetic and
  992. roman numeral processing is necessary.
  993.  
  994. @<Do Sorted Non Blind Entry@>=
  995. with sort_record do begin
  996. if ord(page_string[1]) < ord('0')
  997. then @<Add Roman Page Number@>
  998. else @<Add Arabic Page Number@>;
  999. end;
  1000.  
  1001. @ @<Add Roman Page Number@>=
  1002. begin
  1003. if length(str_build) >= 1
  1004. then str_build:=ccat(',',str_build);
  1005. strvalue(page_number,temp_strvalue);
  1006. str_build := ccat(ccat(ccat(strconv(page_string),
  1007. ','),temp_strvalue),str_build);
  1008. if record_type = '0'
  1009. then str_build := ccat('B',str_build)
  1010. else if record_type = '4'
  1011. then str_build := ccat('U',str_build)
  1012. else if record_type = '6'
  1013. then str_build := ccat('I',str_build)
  1014. end
  1015.  
  1016. @ @<Add Arabic Page Number@>=
  1017. begin
  1018. if length(str_build) >= 1
  1019. then add_comma(str_build);
  1020. if record_type = '0'
  1021. then str_build := ccat(str_build,'B')
  1022. else if record_type = '4'
  1023. then str_build := ccat(str_build,'U')
  1024. else if record_type = '6'
  1025. then str_build := ccat(str_build,'I');
  1026. str_build := ccat(str_build,strconv(page_string));
  1027. end
  1028.  
  1029. @ {\it output\_string} is used to collect all of the page numbers
  1030. before writing them back out to the index file;
  1031. {\it bold\_string} collects the bold page numbers;
  1032. {\it us\_string} collects the underscored page numbers;
  1033. {\it rm\_string} collects everything not bold or underscored.
  1034. As you might expect, {\it italic\_last\_page} is true when the last
  1035. page number of a `roll' is to be italic.
  1036.  
  1037. @<Global Var...@>=
  1038. @!output_string:string_type(max_lrecl);
  1039. @!bold_string:string_type(max_lrecl);
  1040. @!us_string:string_type(max_lrecl);
  1041. @!rm_string:string_type(max_lrecl);
  1042. @!italic_last_page:boolean;
  1043.  
  1044. @^System dependent code@>
  1045. @ {\it finish\_the\_process} is a procedure that is used to
  1046. first check to see if we are {\it on\_a\_roll}, which means we are formatting
  1047. something like pages 1-3.
  1048. After doing that, if there were any bold page numbers, they are concatenated
  1049. to the front of the {\it output\_string}; underscored page numbers (if any)
  1050. are concatenated to the end of the {\it output\_string}.
  1051.  
  1052. @p procedure finish_the_process;
  1053. begin
  1054. @<Concatenate Bold Pages to Output@>;@/
  1055. @<Concatenate Roman Pages to Output@>;@/
  1056. @<Concatenate Underscored Pages to Output@>;
  1057. end;
  1058.  
  1059. @ If there were any bold page numbers, then add them to the {\it
  1060. output\_string}.
  1061.  
  1062. @<Concatenate Bold...@>=
  1063. if length(bold_string) > 0 then begin
  1064. if length(output_string) > 0 then add_cmma_blnk(output_string);
  1065. output_string := ccat(output_string,bold_string);
  1066. end
  1067.  
  1068. @ If we were on a roll (a consecutive series of page numbers) then finish
  1069. the roll. Then if there were any roman or italic page numbers, add them to
  1070. the {\it output\_string}.
  1071.  
  1072. @<Concatenate Roman...@>=
  1073. if on_a_roll then if italic_last_page
  1074. then rm_string :=
  1075. ccat(ccat(ccat(rm_string,'{\it '),trim(prev_pg_string)),'}')
  1076. else rm_string := ccat(rm_string,prev_pg_string);
  1077. @.\bs{}it@>
  1078. if length(rm_string) > 0 then begin if length(output_string) > 0
  1079. then add_cmma_blnk(output_string);
  1080. output_string := ccat(output_string,rm_string);
  1081. end
  1082.  
  1083. @ If there were any underscored page numbers, add them to the {\it
  1084. output\_string} here.
  1085.  
  1086. @<Concatenate Under...@>=
  1087. if length(us_string) > 0 then begin if length(output_string) > 0
  1088. then add_cmma_blnk(output_string);
  1089. output_string := ccat(output_string,us_string);
  1090. end
  1091.  
  1092. @ The procedure {\it start\_digesting} will initialize a few variables,
  1093. then add the {\it current\_page} to the appropriate list.
  1094.  
  1095. @p procedure start_digesting;
  1096. begin @<Initialize Some Global Variables@>;@/
  1097. prev_pg_string := curr_str_page;@/
  1098. @<Do Appropriate Highlighting@>;
  1099. end;
  1100.  
  1101. @ @<Initialize Some Global Variables@>=
  1102. italic_last_page := false;
  1103. bold_string := ''; us_string :=''; rm_string:='';
  1104. starting_to_process:=false;
  1105. on_a_roll:=false
  1106.  
  1107. @ @<Get the Current Print Type of this Page Number@>=
  1108. print_type:= curr_str_page[1];
  1109. if (print_type='B') or (print_type='I') or (print_type='U')
  1110. then curr_str_page:=dosubstr(curr_str_page,2)
  1111. else print_type := ' '
  1112.  
  1113. @ @<Do Appropriate Highlighting@>=
  1114. if print_type = 'B' then do_when_bold
  1115. else if print_type = 'I' then do_when_italic
  1116. else if print_type = 'U' then do_when_underscore
  1117. else do_when_roman
  1118.  
  1119. @^System dependent code@>
  1120. @ The procedure {\it digest\_the\_line} will take the full {\it str\_build}
  1121. and add the dashes when there is a run of page numbers (i.e. ``{\tt 1,2,3}'' bec
  1122. ``1-3''); add the bold page numbers in front of the string (i.e.
  1123. ``{\tt 1,2,B3,3,4}''
  1124. prints as ``{\bf 3},1-4''. Note that page 3 had to appear twice
  1125. ``{\tt B3,3}''. If it had only appeard as a bold page number and not the
  1126. default, ``{\tt 1,2,B3,4}'', you would get ``1-2,{\bf 3},4'');
  1127. add the underscored
  1128. page numbers to the end of the string (similar to bold page numbers,
  1129. ``1,2,U3,3,4'' prints as ``1-4,\us{3}''); and print in italics when
  1130. necessary.
  1131.  
  1132. @p procedure digest_the_line;
  1133. var
  1134. i:integer;
  1135. @!temp_roman:string_type(max_pn_alpha);
  1136. @!no_page_numbers:boolean;
  1137. begin
  1138. @<Process Each Page Number@>;@/
  1139. @<Write the Line Out@>;
  1140. end;
  1141.  
  1142. @ @<Global Var...@>=
  1143. @!starting_to_process :boolean;
  1144. @!print_type:char;
  1145. @!curr_str_page :string_type(max_pn_alpha);
  1146. @!curr_num_page :integer;
  1147.  
  1148. @ Initialize the variables, then read each page number until the whole
  1149. string is digested, then finish off the page number string before returning.
  1150. If {\it str\_build} is null, then we are doing a blind entry with no
  1151. page numbers associated. In that case, initialize the highlighting
  1152. strings.
  1153. @<Process Each Page Number@>=
  1154. output_string := '';
  1155. starting_to_process := true;
  1156. no_page_numbers:=false;
  1157. if length(str_build) > 0
  1158. then repeat @<Digesting Each Page Number@>
  1159.         until length(str_build) < 1
  1160. else if starting_to_process then begin
  1161. @<Initialize Some Global Variables@>;
  1162. no_page_numbers:=true;
  1163. end;
  1164. finish_the_process;
  1165.  
  1166. @ First get the next page number. Then if we are starting, initialize
  1167. everything. If we have already started, then check to see if we have a
  1168. consecutive page number sequence (i.e. pages 1,2,3,4 etc.) which is called
  1169. a `roll'.
  1170. @<Digesting Each Page Number@>=
  1171. @<Get Next Page Number@>;@/
  1172. if starting_to_process
  1173. then start_digesting
  1174. else @<Check for a Roll@>;
  1175. prev_page_number:=curr_num_page;
  1176.  
  1177. @ @<Get Next Page Number@>=
  1178. i := doindex(str_build,',');
  1179. if i < 1 then begin curr_str_page:=str_build; str_build:='';end
  1180. else begin
  1181. curr_str_page:=dosubstr(str_build,1,i-1);
  1182. str_build := dosubstr(str_build,i+1);
  1183. end;
  1184. @<Get the Current Print Type of this Page Number@>;@/
  1185. if ord(curr_str_page[1]) < ord('0') {Then its alphabetic}
  1186. then @<Get Numeric for Roman Page Number@>
  1187. else curr_num_page:=get_numeric(curr_str_page);
  1188.  
  1189. @ @<Get Numeric for Roman Page Number@>=
  1190. begin
  1191. i := doindex(str_build,',');
  1192. if i < 1 then begin temp_roman:=str_build; str_build:='';end
  1193. else begin
  1194. temp_roman:=dosubstr(str_build,1,i-1);
  1195. str_build := dosubstr(str_build,i+1);
  1196. end;
  1197. curr_num_page:=get_numeric(temp_roman);
  1198. end
  1199.  
  1200. @ If we are on a roll, then the previous page number will be one less than
  1201. the current page number. If this is not the case, then {\it
  1202. finish\_the\_process}
  1203. and {\it start\_digesting} all over again.
  1204.  
  1205. @<Check for a Roll@>=
  1206. if ((prev_page_number = curr_num_page) and (print_type <> ' ')) or
  1207.    ((prev_page_number>=0) and (prev_page_number = curr_num_page - 1)) or
  1208.    ((prev_page_number<0) and (prev_page_number = curr_num_page + 1))
  1209. then begin
  1210. @<Got a Roll Going@>
  1211. prev_pg_string:=curr_str_page;
  1212. end
  1213. else begin finish_the_process; start_digesting; end
  1214.  
  1215. @ Ok, so we're on a roll, right? If the {\it print\_type} is ``B'' (bold) or
  1216. ``U'' (underscored) then do those and return. Otherwise, check to se if we
  1217. were previously {\it on\_a\_roll}. If we weren't, then add the hyphen to the
  1218. starting page number. In either case, move the {\it current\_page} to the
  1219. {\it prev\_pg\_string}. Lastly, if the page number is italic, then set
  1220. {\it italic\_last\_page} to true, in case it ends up being the last page in
  1221. the string.
  1222.  
  1223. @<Got a Roll Going@>=
  1224. if print_type = 'B' then do_when_bold
  1225. else if print_type = 'U' then do_when_underscore
  1226. else begin if not on_a_roll then begin on_a_roll := true;
  1227. rm_string:=ccat(rm_string,'--'); end;
  1228. if print_type = 'I' then italic_last_page := true
  1229. else italic_last_page := false;
  1230. end;
  1231.  
  1232. @ Before the page numbers are actually written, we need to write out
  1233. the ID depending on which level we are formatting. Level 1 is the
  1234. primary level, level 2 is the subentry1, and level 3 is subentry2.
  1235. Then, we need to do a couple of things depending on whether there is
  1236. dot leadering going on or not.
  1237. Finally, write the output string to the formatted index file.
  1238.  
  1239. @<Write the Line Out@>=
  1240. write_prev_header;
  1241. if dot_leadering then begin @<Do Dot Leadering@> end
  1242. else begin @<Do not do Dot Leadering@> end;
  1243. do_lnwrite(ix_file,output_string);
  1244.  
  1245. @ If there is dot leadering, then add the blind entry first, before adding
  1246. the leadering.
  1247.  
  1248. @<Do Dot Leadering@>=
  1249. if length(str_blind_entry) > 0
  1250. then begin
  1251. if length(output_string) > 0 then add_cmma_blnk(output_string);
  1252. output_string := ccat(output_string,str_blind_entry);
  1253. end;
  1254. output_string := ccat(ccat('\leader{}',output_string),'\par');
  1255. @.\bs{}leader@>
  1256. @.\bs{}par@>
  1257.  
  1258. @ @<Global Var...@>=
  1259. @!new_level_1:boolean;
  1260.  
  1261. @ If there is not dot leadering, then the blind entry goes at the end of the
  1262. page, except for
  1263. the paragraph style ({\it print\_style}=1). Also, if the paragraph style,
  1264. then do not add the {\tt \bs{}par} at the end. Instead, add a comma
  1265. if there is a new level 1 entry.
  1266.  
  1267. @<Do not do Dot Leadering@>=
  1268. if (print_style <> '1') and (length(str_blind_entry) > 0)@/
  1269. then begin
  1270. if length(output_string) > 0 then add_cmma_blnk(output_string);
  1271. output_string := ccat(output_string,str_blind_entry);
  1272. end;
  1273. if print_style = '1'
  1274. then if new_level_1 and ((length(str_blind_entry)=0) or no_page_numbers)
  1275. then do_nothing
  1276. else add_cmma_blnk(output_string)
  1277. else output_string := ccat(output_string,'\par');
  1278. @.\bs{}par@>
  1279.  
  1280. @ The {\it build\_sorted\_index} procedure is the driving force behind
  1281. formatting the index. The basic process is to read all of the sorted records in
  1282. and create a record called {\it str\_build}. There will be one of these for
  1283. each primary and subentry level index. After doing the {\it str\_build},
  1284. this record is then read through again to add the {\it\TeX T1} markup
  1285. necessary to
  1286. print out the sorted index line.
  1287.  
  1288. @p procedure build_sorted_index;
  1289. var @!first_time_through:boolean;
  1290.  
  1291. @ @<Global Var...@>=
  1292. @!prev_sr1:field_array;
  1293. @!prev_sr2:field_array;
  1294. @!prev_sr3:field_array;
  1295. @!prev_pn1:field_array;
  1296. @!prev_ln1:integer;{the length of pn1}
  1297. @!prev_pn2:field_array;
  1298. @!prev_ln2:integer;{the length of pn2}
  1299. @!prev_pn3:field_array;
  1300. @!prev_ln3:integer;{the length of pn3}
  1301. @!id1_is_missing:boolean;
  1302. @!id2_is_missing:boolean;
  1303.  
  1304. @ The first thing we need to do is read the next sorted record. If this is the
  1305. first time ever through this procedure, then do some quick initializing. Then,
  1306. once we come to a new ID, we should write out the previous index page numbers,
  1307. etc. If the ID has not changed from the previous record, then add the page
  1308. number given here on the sorted record to the {\it str\_build}.
  1309.  
  1310. @p begin first_time_through:=true;
  1311. while not eof(sort_file) do begin
  1312. sort_record:=sort_file@@;
  1313. get(sort_file);
  1314. if first_time_through then begin @<Do First Time Inits@> end;
  1315. if not equal_arrays(prev_sr1,sort_record.sort_part[1].field_level)
  1316. then begin
  1317. @<Start a New Level 1@> end
  1318. else if not equal_arrays(prev_sr2,sort_record.sort_part[2].field_level)
  1319. then begin
  1320. @<Start a New Level 2@> end
  1321. else if not equal_arrays(prev_sr3,sort_record.sort_part[3].field_level)
  1322. and (sort_record.record_type <> '9')
  1323. then begin
  1324. @<Start a New Level 3@> end
  1325. else add_page_number;
  1326. end;
  1327. @<Digest and Print Level 1@>
  1328. end;
  1329.  
  1330. @ First write out the {\it \bs{}everyindex} record at the beginning
  1331. of the index file.
  1332.  
  1333. @<Do First Time Inits@>=
  1334. do_lnwrite(ix_file,'\everyindex{',ix,'}');
  1335. @.\bs{}everyindex@>
  1336.  
  1337. @ These initializations only get done at the beginning of the
  1338. first sort record.
  1339.  
  1340. @<Do First Time Inits@>=
  1341. first_time_through:=false;
  1342. id1_is_missing:=false;
  1343. id2_is_missing:=false;
  1344. new_level_1:=false;
  1345. with sort_record do begin
  1346. prev_sr1:=sort_part[1].field_level;
  1347. prev_sr2:=sort_part[2].field_level;
  1348. prev_sr3:=sort_part[3].field_level;
  1349. prev_pn1:=print[1].field_level;
  1350. prev_ln1:=print[1].field_lngth;
  1351. prev_pn2:=print[2].field_level;
  1352. prev_ln2:=print[2].field_lngth;
  1353. prev_pn3:=print[3].field_level;
  1354. prev_ln3:=print[3].field_lngth;
  1355. end;
  1356. prev_page_number:=0;
  1357. str_blind_entry:='';@/
  1358. str_build:='';@/
  1359.  
  1360. @ If this is not a blind entry, then check to see if sort records for
  1361. the first level id are missing. If they are, then the id name itself
  1362. will be printed in {\it id1\_missing\_check}.
  1363.  
  1364. @<Do First Time Inits@>=
  1365. if sort_record.record_type <> '9'
  1366. then id1_missing_check;
  1367.  
  1368. @ Only alpha (a--z and A--Z) and numeric (0--9) characters get an
  1369. {\it \bs{}everyletterbreak}. Punctuation, for example, will not get
  1370. an {\it \bs{}everyletterbreak}.
  1371.  
  1372. @<Do First Time Inits@>=
  1373. with sort_record.sort_part[1] do begin
  1374. if ((field_level[1]>='a') and (field_level[1]<='i')) or @/
  1375. ((field_level[1]>='j') and (field_level[1]<='r')) or @/
  1376. ((field_level[1]>='s') and (field_level[1]<='z')) or @/
  1377. ((field_level[1]>='A') and (field_level[1]<='I')) or @/
  1378. ((field_level[1]>='J') and (field_level[1]<='R')) or @/
  1379. ((field_level[1]>='S') and (field_level[1]<='Z')) or @/
  1380. ((field_level[1]>='0') and (field_level[1]<='9'))
  1381.  then
  1382. do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{',field_level[1],'}')
  1383.  else
  1384. do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{}');
  1385. end
  1386. @.\bs{}everyletterbreak@>
  1387.  
  1388. @
  1389. First we need to {\it digest\_the\_line} which is the current index's
  1390. entry. {\it digest\_the\_line} will write out the formatted index unless
  1391. we are in the paragraph
  1392. style of formatted index
  1393. ({\it print\_style}=1). If we are in the paragraph style,
  1394. then we need to write out any {\it str\_blind\_entry} pending.
  1395.  
  1396. @<Digest and Print Level 1@>=
  1397. new_level_1:=true;
  1398. digest_the_line;
  1399. if print_style = '1' then
  1400. do_lnwrite(ix_file,str_blind_entry,'\par');
  1401. new_level_1:=false;
  1402. @.\bs{}par@>
  1403.  
  1404. @ We have just found the start of a new primary index level.
  1405. First we need to process the previous index's
  1406. entry.
  1407. If there is a blind entry, and it is style `1', then add it now.
  1408. Next, if we are at a letter break (i.e., going from the sorted ``A''
  1409. primary index letters to the ``B''s) then write the ``{\tt
  1410. \bs{}everyletterbreak}'' to the formatted index file.
  1411.  
  1412. @<Start a New Level 1@>=
  1413. @<Digest and Print Level 1@>
  1414. with sort_record.sort_part[1] do begin
  1415. if (field_level[1] <> prev_sr1[1])
  1416. then if
  1417. (((field_level[1]>='a') and (field_level[1]<='i')) or @/
  1418. ((field_level[1]>='j') and (field_level[1]<='r')) or @/
  1419. ((field_level[1]>='s') and (field_level[1]<='z')) or @/
  1420. ((field_level[1]>='A') and (field_level[1]<='I')) or @/
  1421. ((field_level[1]>='J') and (field_level[1]<='R')) or @/
  1422. ((field_level[1]>='S') and (field_level[1]<='Z')) or @/
  1423. ((field_level[1]>='0') and (field_level[1]<='9'))) @/
  1424. then do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{',field_level[1],'}')
  1425.  else
  1426. do_lnwrite(ix_file,'\everyletterbreak{',ix,'}{}');
  1427. end;
  1428. @.\bs{}everyletterbreak@>
  1429.  
  1430. @ The next thing to be done when starting a new primary index is to
  1431. check the subentries. If they exist, then you can assume that this
  1432. primary level index has no page numbers associated with it. The macro
  1433. {\it id1\_missing\_check} accomplishes this.
  1434.  
  1435. @<Start a New Level 1@>=
  1436. curr_level:=1;@/
  1437. if sort_record.record_type <> '9'
  1438. then id1_missing_check;
  1439.  
  1440. @ The complicated stuff is done. So now we can actually do some simple
  1441. initializations to get this new level 1 (primary level) sort rolling.
  1442.  
  1443. @<Start a New Level 1@>=
  1444. str_build:='';
  1445. str_blind_entry:='';
  1446. with sort_record do begin
  1447. prev_sr1:=sort_part[1].field_level;
  1448. prev_sr2:=sort_part[2].field_level;
  1449. prev_sr3:=sort_part[3].field_level;
  1450. prev_pn1:=print[1].field_level;
  1451. prev_ln1:=print[1].field_lngth;
  1452. prev_pn2:=print[2].field_level;
  1453. prev_ln2:=print[2].field_lngth;
  1454. prev_pn3:=print[3].field_level;
  1455. prev_ln3:=print[3].field_lngth;
  1456. prev_page_number:=0;
  1457. end;
  1458. add_page_number;
  1459.  
  1460. @ We have just found the start of a new subentry1 index level.
  1461. These modules are similar to {\it Start a New Level 1}, but not as
  1462. detailed since it is dealing with fewer levels.
  1463. First we need to {\it digest\_the\_line} which is the previous index's
  1464. entry. {\it digest\_the\_line} will write out the formatted index unless
  1465. we are in the paragraph
  1466. style of formatted index
  1467. ({\it print\_style}=1).
  1468.  
  1469. @<Start a New Level 2@>=
  1470. digest_the_line;
  1471.  
  1472. @ The next thing to be done when starting a new subentry1 index is to
  1473. check the subentry2. If it exists, then you can assume that this
  1474. subentry index has no page numbers associated with it.
  1475. This is done in {\it id2\_missing\_check}.
  1476.  
  1477. @<Start a New Level 2@>=
  1478. id2_missing_check;
  1479.  
  1480. @ The complicated stuff is done. So now we can actually do some simple
  1481. initializations to get this new level 2 (subentry2 level) sort rolling.
  1482.  
  1483. @<Start a New Level 2@>=
  1484. str_build:='';
  1485. if print_style <> '1' then str_blind_entry:='';
  1486. with sort_record do begin
  1487. prev_sr2:=sort_part[2].field_level;
  1488. prev_sr3:=sort_part[3].field_level;
  1489. prev_pn2:=print[2].field_level;
  1490. prev_ln2:=print[2].field_lngth;
  1491. prev_pn3:=print[3].field_level;
  1492. prev_ln3:=print[3].field_lngth;
  1493. prev_page_number:=0;
  1494. end;
  1495. add_page_number;
  1496.  
  1497. @ We have just found the start of a new subentry2 index level.
  1498. These modules are similar to {\it Start a New Level 2}, but not as
  1499. detailed since it is dealing with the lowest level.
  1500. First we need to {\it digest\_the\_line} which is the previous index's
  1501. entry. {\it digest\_the\_line} will write out the formatted index unless
  1502. we are in the paragraph
  1503. style of formatted index
  1504. ({\it print\_style}=1).
  1505.  
  1506. @<Start a New Level 3@>=
  1507. digest_the_line;
  1508. curr_level:=3;
  1509.  
  1510. @ Do some
  1511. initializations to get this new level 3 (subentry2) sort rolling.
  1512.  
  1513. @<Start a New Level 3@>=
  1514. str_build:='';
  1515. if print_style <> '1' then str_blind_entry:='';
  1516. with sort_record do begin
  1517. prev_sr3:=sort_part[3].field_level;
  1518. prev_pn3:=print[3].field_level;
  1519. prev_ln3:=print[3].field_lngth;
  1520. prev_page_number:=0;
  1521. end;
  1522. add_page_number;
  1523.  
  1524. @* Main Program.
  1525. Ok, here is the main program. First we initialize (all\_blanks); then
  1526. set the ix\_file for input and the sort\_file for output; read all of the
  1527. entries, processing each one; close the files; sort; read the sorted file
  1528. in and build the entries, writing them back to the ix\_file to be read in
  1529. by the index markup. WHEW!!
  1530.  
  1531. @p
  1532. begin @<Initialize Main@>@/
  1533. termout(messages);
  1534. reset_file(ix);@/
  1535. file_rewrite(s_file_number); {Should always be file 9}@/
  1536. read_all_entries;@/
  1537. close(ix_file);@/
  1538. close(sort_file);@/
  1539. @<Sort the Index@>@/
  1540. reset_file(s_file_number);@/
  1541. file_rewrite(ix);@/
  1542. build_sorted_index;@/
  1543. end;
  1544.  
  1545. @ @<Initialize Main@>=
  1546. for i := 1 to max_field do all_blanks[i] := ' ';
  1547.  
  1548. @ As mentioned earlier, {\it plsort} is an external PL/1 subroutine
  1549. @^PL/1@>
  1550. @^Syncsort@>
  1551. which is used to call Syncsort to sort the file.
  1552. The sort fields are as follows:
  1553. \halign{\hskip3em\hfill#\hfill&&\hskip3em\hfill#\hfill\cr
  1554. \bf Starting Column&\bf Length&\bf Field Description&\bf Order\cr
  1555. 38&300&character&ascending\cr
  1556. 342&300&character&ascending\cr
  1557. 646&300&character&ascending\cr
  1558. 10&4&binary&descending\cr
  1559. 6&4&binary&ascending\cr}
  1560.  
  1561. @<Global Var...@>=
  1562. sort_rc:integer;
  1563.  
  1564. @ @<Sort the Index@>=
  1565. plsort(sort_rc);
  1566. if sort_rc = 0
  1567.    then writeln(messages,'Index Successfully Completed')
  1568.    else writeln(messages,'Index Failed');
  1569.  
  1570. @* Index.
  1571. All modules in which an identifier is
  1572. used are listed with that identifier, except that reserved words are
  1573. indexed only when they appear in format definitions, and the appearances
  1574. of identifiers in module names are not indexed. Underlined entries
  1575. correspond to where the identifier was declared. Error messages, control
  1576. sequences put into the output, and a few
  1577. other things like ``Syncsort'' are indexed here too.
  1578.